package com.common.utils;

import com.common.constants.StringConstantPool;
import com.common.interfaces.MyFunction;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @Brief 通用树的构造工具
 * @Author chenyl
 * @Date 2021-12-10
 * @Version 1.0.0
 */
public class TreeUtil {
    /**
     * 通用树的构造函数
     * 实体类无需任何继承，也不需要自带子级集合，但返回值为List<Map>结构
     * 所有参数均不可为空
     *
     * @param dataList            源数据集合
     * @param paramFunction       主键名称
     * @param parentParamFunction 上级主键名称
     * @param <T>                 对应的实体类
     * @return
     */
    public static <T> List<Map<String, Object>> buildTree(Collection<T> dataList, MyFunction<T, ?> paramFunction, MyFunction<T, ?> parentParamFunction) {
        // 将属性函数组装成一个数组
        MyFunction[] functionArr = {paramFunction, parentParamFunction};
        // 获取属性名称
        String[] columns = FieldUtil.getColumns(functionArr);
        // 组装数据并返回
        return generateTree(dataList, columns[0], columns[1], StringConstantPool.CHILD_PARAM_NAME);
    }

    /**
     * 通用树的构造函数
     * 实体类无需任何继承，也不需要自带子级集合，但返回值为List<Map>结构
     * 所有参数均不可为空
     *
     * @param sourceList          源数据集合
     * @param paramFunction       主键GET函数
     * @param parentParamFunction 上级主键GET函数
     * @param childParamFunction  子集合GET函数
     * @param <T>                 对应的实体类
     * @return
     */
    public static <T> List<Map<String, Object>> buildTree(Collection<T> sourceList,
                                                          MyFunction<T, ?> paramFunction,
                                                          MyFunction<T, ?> parentParamFunction,
                                                          MyFunction<T, ?> childParamFunction) {
        // 将属性函数组装成一个数组
        MyFunction[] functionArr = {paramFunction, parentParamFunction, childParamFunction};
        // 获取属性名称
        String[] columns = FieldUtil.getColumns(functionArr);
        // 组装数据并返回
        return generateTree(sourceList, columns[0], columns[1], columns[2]);
    }


    /**
     * 生成树形结构
     *
     * @param sourceList 源数据
     * @param param      主键
     * @param child      子级名称
     * @return 树形结构集合
     */
    private static <T> List<Map<String, Object>> generateTree(Collection<T> sourceList,
                                                              String param,
                                                              String parentParam,
                                                              String child) {
        // 判断数据是否有值
        if (CollectionUtils.isEmpty(sourceList)) {
            throw new NullPointerException("sourceList is null or size is null");
        }
        // 创建主键集合
        Set<Object> paramSet = new HashSet<>();
        // 创建子集合
        Map<Object, List<Map<String, Object>>> childMap = new HashMap<>();
        // 创建树形结构集合
        List<Map<String, Object>> beanMapList = new ArrayList<>();
        for (T t : sourceList) {
            // 将对象转换成Map
            Map<String, Object> map = new HashMap<String, Object>(BeanMap.create(t));
            // 将主键值放入集合中备用
            paramSet.add(map.get(param));
            // 创建对象中的子级集合
            map.put(child, new ArrayList<>());
            // 将转化后的map放入到待处理集合中
            beanMapList.add(map);
            // 将主键与子级集合的关联关系放入到子级map中
            childMap.put(map.get(param), (List<Map<String, Object>>) map.get(child));
        }
        // 获取迭代器
        Iterator<Map<String, Object>> iterator = beanMapList.iterator();
        // 遍历并组装数据
        while (iterator.hasNext()) {
            Map<String, Object> map = iterator.next();
            // 判断上级主键是否存在主键集合中
            if (paramSet.contains(map.get(parentParam))) {
                // 将数据放入到上级主键对应的子集合中
                childMap.get(map.get(parentParam)).add(map);
                // 删除已装入子级的元素
                iterator.remove();
            }
        }
        // 返回结果集
        return beanMapList;
    }
}
